/*完成日志器模块
    1.抽象日志器基类
    2.派生出不同的子类（同步日志器类 & 异步日志器类）
    同步日志器：直接将日志消息进行格式化写入文件
    异步日志器：为了避免因为写日志的过程阻塞，导致业务再写日志的时候影响效率，
    因此异步思想就是不让业务线程进行日志的实际落地操作，而是将日志消息放到缓冲区（一块指定的内存）中
    接下来让一个专门的异步线程，去针对缓冲区的数据进行处理（实际的落地操作）
        2.1 实现一个线程安全的缓冲区
        2.2 创建一个异步工作线程，专门负责缓冲区中日志消息的落地操作
            2.2.1 缓冲区设计 （不涉及空间的频繁申请与释放，否则会降低效率）
                环形队列（提前将空间申请好，然后对空间循环利用）
                缓冲区的操作会涉及多线程，因此缓冲区的操作要加锁，且写日志操作实际开发中不会分配过多资源,工作线程有一个日志器即可
                由于每一次读写操作都存在锁冲突问题，较为严重，采用双缓冲区方案：
                （双缓冲区是处理器将⼀个缓冲区中的任务全部处理完毕后，然后交换两个缓冲区，重新对新的缓冲区中的任务进⾏处理，虽然同时多线程写⼊也会冲突，但
                是冲突并不会像每次只处理⼀条的时候频繁（减少了⽣产者与消费者之间的锁冲突），且不涉及到空间的频繁申请释放所带来的消耗。）
            优势：避免了空间的频繁申请释放，且尽可能的减少了⽣产者与消费者之间锁冲突的概率，提⾼了任务处理效率。
*/

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "message.hpp"
#include "looper.hpp"

#include <stdarg.h>
#include <iostream>
#include <vector>
#include <atomic>
#include <mutex>
#include <unordered_map>
namespace log
{
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, LogLevel::value level,
               Formatter::ptr &formatter,
               std::vector<LogSink::ptr> &sinks)
            : _logger_name(logger_name), _limit_level(level),
              _formatter(formatter), _sinks(sinks.begin(), sinks.end())
        {
        }
        const std::string &name() { return _logger_name; }
        /*完成构造日志消息对象并进行格式化,得到格式化字符串 -- 然后进行落地*/
        void debug(const std::string &filename, size_t line, const std::string &fmt, ...)
        {
            // 1.判断是否达到输出等级
            if (LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            // 2.对fmt格式化字符串和不定参字符串进行组织，得到日志消息的字符串
            va_list al;
            va_start(al, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), al);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(al);
            serialize(LogLevel::value::DEBUG, filename, line, res);
            free(res);
        }
        void info(const std::string &filename, size_t line, const std::string &fmt, ...)
        {
            // 1.判断是否达到输出等级
            if (LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            // 2.对fmt格式化字符串和不定参字符串进行组织，得到日志消息的字符串
            va_list al;
            va_start(al, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), al);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(al);
            serialize(LogLevel::value::INFO, filename, line, res);
            free(res);
        }
        void warn(const std::string &filename, size_t line, const std::string &fmt, ...)
        {
            // 1.判断是否达到输出等级
            if (LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            // 2.对fmt格式化字符串和不定参字符串进行组织，得到日志消息的字符串
            va_list al;
            va_start(al, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), al);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(al);
            serialize(LogLevel::value::WARN, filename, line, res);
            free(res);
        }
        void error(const std::string &filename, size_t line, const std::string &fmt, ...)
        {
            // 1.判断是否达到输出等级
            if (LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            // 2.对fmt格式化字符串和不定参字符串进行组织，得到日志消息的字符串
            va_list al;
            va_start(al, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), al);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(al);
            serialize(LogLevel::value::ERROR, filename, line, res);
            free(res);
        }
        void fatal(const std::string &filename, size_t line, const std::string &fmt, ...)
        {
            // 1.判断是否达到输出等级
            if (LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            // 2.对fmt格式化字符串和不定参字符串进行组织，得到日志消息的字符串
            va_list al;
            va_start(al, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), al);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(al);
            serialize(LogLevel::value::FATAL, filename, line, res);
            free(res);
        }

    protected:
        void serialize(LogLevel::value level, const std::string &filename, size_t line, char *str)
        {
            // 3.构造LogMessage对象
            LogMessage msg(level, line, filename, _logger_name, str);
            // 4.通过格式化工具对LogMessage进行格式化,得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5.进行日志落地
            log(ss.str().c_str(), ss.str().size());
        }
        /*抽象接口完成实际落地输出--不同日志器有不同的实际落地方式*/
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::mutex _mutex;
        std::string _logger_name;                  // 日志器名称
        std::atomic<LogLevel::value> _limit_level; // 原子对象 限制等级
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &logger_name, LogLevel::value level,
                   Formatter::ptr &formatter,
                   std::vector<LogSink::ptr> &sinks)
            : Logger(logger_name, level, formatter, sinks)
        {
        }

    protected:
        // 同步日志器
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &logger_name, LogLevel::value level,
                    Formatter::ptr &formatter,
                    std::vector<LogSink::ptr> &sinks,
                    AsyncType looper_type) // 由realSinkLog,通过绑定函数，形成一个新的函数，且只传一个参数
            : Logger(logger_name, level, formatter, sinks), _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realSinkLog, this, std::placeholders::_1), looper_type))
        {
        }
        // 将数据写入缓冲区
        void log(const char *data, size_t len)
        {
            _looper->push(data, len);
        }
        // 实地落地函数(将缓冲区中的数据落地)
        void realSinkLog(Buffer &buf)
        {
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readableSize()); // 传消费缓冲区可读数据大小,进行实地落地
            }
        }

    private:
        AsyncLooper::ptr _looper;
    };

    /*使用建造者模式建造日志器*/
    // 抽象一个日志器建造者类（完成日志器对象所需零部件的构建 & 日志器的构建）
    // 设置日志器类型，将不同类型的日志器的创建放到同一个日志器建造者类中完成
    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };

    class LoggerBuilder
    {
    public:
        LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNC), _limit_level(LogLevel::value::DEBUG), _looper_type(AsyncType::ASYNC_SAFE) {}
        void buildEnableUnsafeAsync() { _looper_type = AsyncType::ASYNC_UNSAFE; }
        void buildLoggerType(LoggerType type) { _logger_type = type; }
        void buildLoggerName(const std::string &name) { _logger_name = name; }
        void buildLoggerLevel(log::LogLevel::value level) { _limit_level = level; }
        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        template <typename SinkType, typename... Args>
        void buildSink(Args &&...args)
        {
            LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(psink);
        }

        virtual Logger::ptr build() = 0;

    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;     // 日志器名称
        LogLevel::value _limit_level; // 限制等级
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    // 派生出具体的建造者类 -- 局部日志器的建造者 & 全局的日志器建造者（全局单例管理器使用）
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须存在日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }

            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
    };

    // 日志器管理器
    class LoggerManager
    {
    public:
        // 懒汉模式--延迟加载
        static LoggerManager &getInstance()
        {
            // 特殊的单例模式，再c++11后满足静态变量线程安全后能满足唯一的构造和析构
            // 当静态局部变量在没有构造之前，其他线程会进入阻塞
            static LoggerManager eton;
            return eton;
        }
        void add(Logger::ptr &logger)
        {
            if (hasLogger(logger->name()))
                return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        // 是否存在某个日志器
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
                return false;
            return true;
        }
        Logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
            {
                return Logger::ptr();
            }
            return it->second;
        }
        // 获取默认日志器
        Logger::ptr rootLogger()
        {
            return _root_logger;
        }

    private:
        LoggerManager()
        {
            //默认日志器只能用本地建造者构建，全局会造成死循环
            std::unique_ptr<log::LoggerBuilder> builder(new log::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.insert(std::make_pair("root", _root_logger));
        }
        LoggerManager(const LoggerManager &) = delete;
        LoggerManager &operator=(const LoggerManager &) = delete;

    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;                              // 默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers; // 日志器
    };

    // 方便用户使用设计一个全局日志器建造者,新增功能:将日志器添加到单例对象中
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty()); // 必须存在日志器名称
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if (_sinks.empty())
            {
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if (_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManager::getInstance().add(logger);
            return logger;
        }
    };
}

#endif